package com.coder.rental.controller;

import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWTPayload;
import com.coder.rental.entity.Permission;
import com.coder.rental.entity.User;
import com.coder.rental.security.CustomerAuthenticationException;
import com.coder.rental.service.IUserService;
import com.coder.rental.utils.JwtUtils;
import com.coder.rental.utils.RedisUtils;
import com.coder.rental.utils.Result;
import com.coder.rental.utils.RouteTreeUtils;
import com.coder.rental.vo.RouteVO;
import com.coder.rental.vo.TokenVo;
import com.coder.rental.vo.UserInfoVo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author caelan
 * @project auto_rental
 */
@RestController
@RequestMapping("/rental/auth")
public class AuthController {
    @Resource
    private RedisUtils redisUtils;

    @Resource
    private IUserService userService;

    @PostMapping("/refreshToken")
    public Result refreshToken(HttpServletRequest request){
        //获取token
        String token = request.getHeader("token");
        if (!StrUtil.isEmpty(token)){
            token = request.getParameter("token");
        }

        Authentication authentication = SecurityContextHolder
                .getContext().getAuthentication();
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        //从token中获取用户名
        String username = JwtUtils.parseToken(token).getClaim("username").toString();
        String newToken = "";
        if (StrUtil.equals(username,userDetails.getUsername())){
            Map<String,Object> map=new HashMap<>();
            map.put("username",userDetails.getUsername());
            newToken= JwtUtils.createToken(map);
        }else{
            throw new CustomerAuthenticationException("token数据异常");
        }
        //获取本次token过期时间
        NumberWithFormat claim= (NumberWithFormat) JwtUtils.parseToken(newToken).getClaim(JWTPayload.EXPIRES_AT);
        DateTime dateTime = (DateTime) claim.convert(DateTime.class, claim);
        long expireTime=dateTime.getTime();
        //创建一个tokenvo的对象，将新生成的token和过期时间存入
        TokenVo tokenVo = new TokenVo();
        tokenVo.setToken(newToken).setExpireTime(expireTime);
        //清除原有token
        redisUtils.del("token:"+token);
        //将新的token存入redis
        long nowTime= DateTime.now().getTime();
        String newTokenKey="token:"+newToken;
        redisUtils.set(newTokenKey,newToken,(expireTime-nowTime)/1000);
        return Result.success(tokenVo).setMessage("刷新token成功");
    }


    /**
 * 获取用户信息
 * 该方法通过Spring Security框架获取当前认证用户的详细信息包括权限代码
 * 并以UserInfoVo对象的形式返回这些信息
 *
 * @return Result对象封装了UserInfoVo对象，表示用户信息获取结果
 */
    @GetMapping("/getInfo")
    public Result getUserInfo(){
        //从SecurityContextHolder上下文中获取认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //如果认证信息为空，则返回失败结果
        if (authentication == null){
            return Result.fail().setMessage("认证信息为空");
        }

        //获取用户信息
        User user = (User) authentication.getPrincipal();
        //将用户权限列表转换为权限代码数组
        List<Permission> permissionList = user.getPermissionList();
        Object[] array = permissionList.stream().filter(Objects::nonNull)
                .map(Permission::getPermissionCode)
                .toArray();
        //创建UserInfoVo对象，包含用户ID、用户名、头像、昵称和权限代码数组
        UserInfoVo userInfoVo = new UserInfoVo(user.getId(), user.getUsername(), user.getAvatar(), user.getNickname(),array);
        //返回成功结果，包含用户信息
        return Result.success(userInfoVo).setMessage("获取用户信息成功");
    }



    @GetMapping("/menuList")
    public Result getMenuList() {
        //获取当前用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return Result.fail().setMessage("认证信息为空");
        }
        User user = (User) authentication.getPrincipal();
        //获取用户的权限列表
        List<Permission> permissionList = user.getPermissionList();
        //获取用户的菜单
        //将permission_type为2的按钮移除，不需要生成对应的菜单
        permissionList.removeIf(permission -> Objects.equals(permission.getPermissionType(), 2));
        List<RouteVO> routeVOList = RouteTreeUtils.buildRouteTree(permissionList, 0);
        return Result.success(routeVOList).setMessage("获取菜单列表成功");
    }


        /**
     * 处理用户登出请求的控制器方法
     * 该方法通过Post请求接收用户的登出请求，并执行相应的登出操作
     *
     * @param request 用于获取请求信息，如头部信息和请求参数
     * @param response 用于向客户端返回登出结果
     * @return 返回一个Result对象，包含登出操作的结果信息
     */
    @PostMapping("/logout")
    public Result logout(HttpServletRequest request, HttpServletResponse response){
        // 尝试从请求头部获取token，如果未找到，则尝试从请求参数中获取
        String token = request.getHeader("token");
        if (StrUtil.isEmpty(token)){
            token = request.getParameter("token");
        }

        // 获取当前安全上下文中的认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // 如果存在认证信息，则执行登出操作
        if (authentication != null){
            // 用户一旦登出系统，则清除redis中的token
            redisUtils.del("token:"+token);

            // 使用SecurityContextLogoutHandler处理登出操作
            SecurityContextLogoutHandler handler = new SecurityContextLogoutHandler();
            handler.logout(request,response,authentication);

            // 返回登出成功的响应
            return Result.success().setMessage("登出成功");
        }

        // 如果没有找到认证信息，返回登出失败的响应
        return Result.fail().setMessage("登出失败");
    }

}
